﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using NUnit.Framework;

using NUnitResults;

namespace NUnitResultsTests.Unit
{
    /// <summary>
    /// Tests the TestCase class.
    /// </summary>
    public class TestCaseTests
    {
        #region Construction

        /// <summary>
        /// Tests that when a TestCase is constructed, it takes the specified Status as
        /// a value for its Status property.
        /// </summary>
        [Theory]
        public void TestStatus(Status expectedStatus)
        {
            var t = new TestCase("Name", "Namespace", expectedStatus, TimeSpan.FromSeconds(1), 1);
            Assert.AreEqual(expectedStatus, t.Status);
        }

        /// <summary>
        /// Tests the name parameter on TestCase's constructor.
        /// </summary>
        [Test]
        [TestCase(null, ExpectedException = typeof(ArgumentNullException))]
        [TestCase("")]
        [TestCase("Name")]
        [TestCase("Some.Other.Valid.Test.Name")]
        [TestCase("Some.Other.Valid.Test.Name(With, Some, \"Params\"")]
        public void TestName(String name)
        {
            var t = new TestCase(name, "Namespace", Status.Inconclusive, TimeSpan.FromSeconds(1), 1);
            Assert.AreEqual(name, t.Name);
        }

        /// <summary>
        /// Tests the namespace parameter on TestCase's constructor.
        /// </summary>
        /// <param name="nspace"></param>
        [Test]
        [TestCase(null, ExpectedException = typeof(ArgumentNullException))]
        [TestCase("")]
        [TestCase("Namespace")]
        [TestCase("Namespace.Namespace2")]
        public void TestNamespace(String nspace)
        {
            var t = new TestCase("Name", nspace, Status.Success, TimeSpan.FromSeconds(1), 1);
            Assert.AreEqual(nspace, t.Namespace);
        }

        /// <summary>
        /// Tests the time parameter on TestCase's constructor.
        /// </summary>
        /// <param name="time"></param>
        [Test]
        [TestCaseSource("GetTimeTestCases")]
        public void TestTime(Nullable<TimeSpan> time)
        {
            var t = new TestCase("Name", "Namespace", Status.Success, time, 1);
            Assert.AreEqual(time, t.Time);
        }

        /// <summary>
        /// Test cases for the time parameter test.
        /// </summary>
        /// <returns></returns>
        public IEnumerable<TestCaseData> GetTimeTestCases()
        {
            yield return new TestCaseData(null);
            yield return new TestCaseData(TimeSpan.FromHours(-12.322342));
            yield return new TestCaseData(TimeSpan.FromSeconds(-1));
            yield return new TestCaseData(TimeSpan.FromSeconds(0));
            yield return new TestCaseData(TimeSpan.FromSeconds(1));
            yield return new TestCaseData(TimeSpan.FromHours(3.4243254));
        }

        /// <summary>
        /// Tests the asserts parameter on TestCase's constructor.
        /// </summary>
        /// <param name="asserts"></param>
        [TestCase(null)]
        [TestCase(-342)]
        [TestCase(-1)]
        [TestCase(0)]
        [TestCase(1)]
        [TestCase(556)]
        public void TestAsserts(Nullable<Int32> asserts)
        {
            var t = new TestCase("Name", "Namespace", Status.Success, TimeSpan.FromSeconds(1), asserts);
            Assert.AreEqual(asserts, t.Asserts);
        }

        #endregion

        #region Equals

        /// <summary>
        /// Tests TestCase.Equals when comparing with either null or an
        /// object which is not a TestCase.
        /// </summary>
        [Test]
        [TestCase(null, Result = false)]
        [TestCase("", Result = false)]
        [TestCase(7, Result = false)]
        public Boolean TestEqualsInvalid(Object arg)
        {
            return new TestCase("", "", Status.Success, null, null).Equals(arg);
        }

        /// <summary>
        /// Tests the TestCase.Equals override.
        /// </summary>
        [Test]
        [TestCaseSource("GetEqualsTestCases")]
        public Boolean TestEquals(TestCase testCase1, TestCase testCase2)
        {
            var result1 = testCase1.Equals(testCase2);
            var result2 = testCase2.Equals(testCase1);
            Assert.IsTrue(result1 == result2, "TestCase.Equals is non-communtative");

            if (result1)
            {
                // If the two objects are equal, they should have the same hash code.
                Assert.IsTrue(testCase1.GetHashCode() == testCase2.GetHashCode());
            }

            return result1;
        }

        /// <summary>
        /// Produces test cases for the Equals method.
        /// </summary>
        /// <returns></returns>
        private IEnumerable<TestCaseData> GetEqualsTestCases()
        {
            yield return new TestCaseData(
                new TestCase("Case1", "Namespace1", Status.Success, TimeSpan.FromSeconds(1), 1), 
                new TestCase("Case1", "Namespace1", Status.Success, TimeSpan.FromSeconds(1), 1)).Returns(true);
            yield return new TestCaseData(
                new TestCase("Case1", "Namespace1", Status.Success, TimeSpan.FromSeconds(1), 1), 
                new TestCase("Case2", "Namespace1", Status.Success, TimeSpan.FromSeconds(1), 1)).Returns(false);
            yield return new TestCaseData(
                new TestCase("Case1", "Namespace1", Status.Success, TimeSpan.FromSeconds(1), 1), 
                new TestCase("Case1", "Namespace2", Status.Success, TimeSpan.FromSeconds(1), 1)).Returns(false);
            yield return new TestCaseData(
                new TestCase("Case1", "Namespace1", Status.Success, TimeSpan.FromSeconds(1), 1), 
                new TestCase("Case1", "Namespace1", Status.Ignored, TimeSpan.FromSeconds(1), 1)).Returns(false);
            yield return new TestCaseData(
                new TestCase("Case1", "Namespace1", Status.Success, TimeSpan.FromSeconds(1), 1), 
                new TestCase("Case1", "Namespace1", Status.Success, TimeSpan.FromSeconds(0.999), 1)).Returns(false);
            yield return new TestCaseData(
                new TestCase("Case1", "Namespace1", Status.Success, TimeSpan.FromSeconds(1), 1), 
                new TestCase("Case1", "Namespace1", Status.Success, TimeSpan.FromSeconds(1), 2)).Returns(false);
        }

        #endregion

        #region Properties

        /// <summary>
        /// Tests the Count property.
        /// </summary>
        [Test]
        public void TestCount()
        {
            var t = new TestCase("Name", "Namespace", Status.Ignored, TimeSpan.FromSeconds(1), 1);
            Assert.AreEqual(1, t.TestCaseCount.Total);
        }

        /// <summary>
        /// Tests the FullName property.
        /// </summary>
        [Test]
        [TestCase("", "TestCaseName", Result = "TestCaseName")]
        [TestCase("Name.Space", "", Result = "Name.Space")]
        [TestCase("Name.Space", "TestCaseName", Result = "Name.Space.TestCaseName")]
        public String TestFullName(String nspace, String name)
        {
            var t = new TestCase(name, nspace, Status.Success, TimeSpan.FromSeconds(1), 1);
            return t.FullName;
        }

        #endregion
    }
}
